﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xrm.Sdk.Query;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Messages;

using QuickGraph;

namespace CrmWorkflowDependencies.Dependencies
{
    using Crm.Extension;

    public static class DependenciesExts
    {
        /// <summary>
        /// Только Status!!!/
        /// Нахрена отдельная метадата для статуса??? Оба статуса наследуют от EnumAttributeMetadata
        /// </summary>
        /// <param name="crm"></param>
        /// <param name="entityname"></param>
        /// <param name="picklistAttributeName"></param>
        /// <returns></returns>
        public static Dictionary<int, string> GetStatusValueLabels(this IOrganizationService crm, string entityname, string picklistAttributeName)
        {
            var rar = (RetrieveAttributeResponse)crm.Execute(new RetrieveAttributeRequest { EntityLogicalName = entityname, LogicalName = picklistAttributeName });
            DependenciesException.ThrowIf(!(rar.AttributeMetadata is StatusAttributeMetadata), "rar.AttributeMetadata must be typeof StatusAttributeMetadata");

            var sam = (StatusAttributeMetadata)rar.AttributeMetadata;

            DependenciesException.ThrowIf(sam.OptionSet == null, "sam.OptionSet must be not null");
            DependenciesException.ThrowIf(sam.OptionSet.Options.Count == 0, "there is not options in {0}", picklistAttributeName);

            var dic = new Dictionary<int, string>();

            foreach (var o in sam.OptionSet.Options)
            {
                dic.Add(o.Value.Value, o.Label.UserLocalizedLabel != null ? o.Label.UserLocalizedLabel.Label : "без метки");
            }

            return dic;
        }

        public static Vertex GetEntityVertex(this IOrganizationService crm, string logicalname, Guid? metadataid)
        {
            RetrieveEntityResponse rer;
            if (string.IsNullOrEmpty(logicalname))
            {
                rer = (RetrieveEntityResponse)crm.Execute(new RetrieveEntityRequest { MetadataId = metadataid.Value });
            }
            else
            {
                rer = (RetrieveEntityResponse)crm.Execute(new RetrieveEntityRequest { LogicalName = logicalname });
            }

            return new Vertex(rer.EntityMetadata.LogicalName, rer.EntityMetadata.DisplayName != null ? rer.EntityMetadata.DisplayName.UserLocalizedLabel.Label : rer.EntityMetadata.LogicalName) { Type = componenttype.Entity };
        }

        private static void AddPropertyIfTrue(this Vertex v, Entity e, string propname)
        {
            if (e.Contains(propname) )
            {
                if (e[propname] is bool)
                {
                    if ((bool)e[propname]) /// добавляем толкьое сли true
                    {
                        v.Properties[propname] = true;
                    }
                }
                else
                {
                    v.Properties[propname] = e[propname];
                }
            }
        }

        public static Vertex BuildVertexFrom(this IOrganizationService crm, Entity dependency, string dependencytype)
        {
            var ct = (componenttype)dependency.Attributes.GetPicklistValue(dependencytype + "type"); /// "dependentcomponenttype"
            Guid oid = (Guid)dependency.Attributes[dependencytype + "objectid"]; /// "dependentcomponentobjectid"

            Vertex vert;
            switch (ct)
            { 
                case componenttype.Workflow:
                    var dwf = crm.FindRecord("workflow", oid);
                    vert = new Vertex(dwf.Id, (string)dwf["name"]);

                    /// добавить properties
                    vert.AddPropertyIfTrue(dwf, "triggeroncreate");
                    vert.AddPropertyIfTrue(dwf, "triggerondelete");
                    vert.AddPropertyIfTrue(dwf, "triggeronupdateattributelist");
                    vert.AddPropertyIfTrue(dwf, "ondemand");
                    vert.AddPropertyIfTrue(dwf, "subprocess");
                    vert.AddPropertyIfTrue(dwf, "xaml");
                    vert.AddPropertyIfTrue(dwf, "primaryentity");

                    break;
                case componenttype.Entity:
                    vert = crm.GetEntityVertex(null, oid);
                    break;
                case componenttype.Attribute:
                    var rar = (RetrieveAttributeResponse)crm.Execute(new RetrieveAttributeRequest { MetadataId = oid });

                    var parentEntity = crm.GetEntityVertex(rar.AttributeMetadata.EntityLogicalName, null);

                    if (rar.AttributeMetadata.DisplayName.UserLocalizedLabel != null)
                    {
                        vert = new VertexProperty(parentEntity, rar.AttributeMetadata.LogicalName, rar.AttributeMetadata.DisplayName.UserLocalizedLabel.Label);
                    }
                    else
                    {
                        vert = new VertexProperty(parentEntity, rar.AttributeMetadata.LogicalName, rar.AttributeMetadata.LogicalName);
                    }
                    break;
                //case componenttype.Relationship:
                //    var rrr = (RetrieveRelationshipResponse)crm.Execute(new RetrieveRelationshipRequest { MetadataId = oid });

                //    var parentEntity = new Vertex(rrr.RelationshipMetadata.ent, rar.AttributeMetadata.EntityLogicalName);

                //    vert = new VertexProperty(rrr.RelationshipMetadata.SchemaName, rrr.RelationshipMetadata.SchemaName);
                //    break;
                default:
                    vert = new Vertex(oid, oid.ToString());
                    break;
            }

            vert.Type = ct;

            return vert;
        }

        public static Entity RetrieveWorkflowSolutionComponent(this IOrganizationService crm, Guid solutionid, Guid wfid)
        {
            QueryByAttribute query = new QueryByAttribute("solutioncomponent");
            query.AddAttributeValue("solutionid", solutionid);
            query.AddAttributeValue("componenttype", (int)componenttype.Workflow);
            query.AddAttributeValue("objectid", wfid);
            var dce = crm.RetrieveMultiple(query).Entities;
            return dce.FirstOrDefault();
        }

        public static DataCollection<Entity> RetrieveRequiredComponents(this IOrganizationService crm, Guid workflowid, componenttype type)
        {
            var response = (RetrieveRequiredComponentsResponse)crm.Execute(new RetrieveRequiredComponentsRequest { ObjectId = workflowid, ComponentType = (int)type });
            return response.EntityCollection.Entities;
        }

        public static DataCollection<Entity> RetrieveRequiredComponents(this IOrganizationService crm, Guid workflowid)
        { 
            /// Перебираем возможные типы зависимостей, а не весь enum, экономия
            var response = (RetrieveRequiredComponentsResponse)crm.Execute(new RetrieveRequiredComponentsRequest { ObjectId = workflowid, ComponentType = (int)componenttype.Workflow });
            return response.EntityCollection.Entities;
        }

        public static DataCollection<Entity> RetrieveWorkflowDependencies(this IOrganizationService crm, Guid workflowid)
        {   
            var response = (RetrieveDependentComponentsResponse)crm.Execute(new RetrieveDependentComponentsRequest { ObjectId = workflowid, ComponentType = (int)componenttype.Workflow });
            return response.EntityCollection.Entities; //// добавить
        }

        public static DataCollection<Entity> RetrieveDependencies(this IOrganizationService crm, Guid workflowid, componenttype type)
        {
            var response = (RetrieveDependentComponentsResponse)crm.Execute(new RetrieveDependentComponentsRequest { ObjectId = workflowid, ComponentType = (int)type });
            return response.EntityCollection.Entities; //// добавить
        }

        public static DataCollection<Entity> RetrieveRegardingWorkflows(this IOrganizationService crm, string regardingEntityTypeName, bool onlyactive = true)
        {
            var metaResponse = (RetrieveEntityResponse)crm.Execute(new RetrieveEntityRequest { LogicalName = regardingEntityTypeName, EntityFilters = EntityFilters.Entity });

            QueryExpression query = new QueryExpression("workflow");
            query.ColumnSet = new ColumnSet("name", "xaml", "subprocess", "ondemand", "triggerondelete", "asyncautodelete", "inputparameters", "triggeroncreate", "triggeronupdateattributelist", "scope");
            //query.ColumnSet = new ColumnSet(true);
            query.Criteria.AddCondition("primaryentity", ConditionOperator.Equal, metaResponse.EntityMetadata.ObjectTypeCode.Value);
            query.Criteria.AddCondition("statuscode", ConditionOperator.Equal, 2); /// Активирован
            query.Criteria.AddCondition("type", ConditionOperator.Equal, 1); /// Активация

            return crm.RetrieveMultiple(query).Entities;
        }
    }
}
